using BepuPhysics.Collidables;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace BepuPhysics.CollisionDetection
{
    public interface ICollisionCallbacks
    {
        // TODO：将来,延续将需要能够接受类型化的冲突缓存。PairCache将存储缓存的分隔轴,用于船体-船体加速和类似的事情。
        unsafe void OnPairCompleted<TManifold>(int pairId, ref TManifold manifold) where TManifold : unmanaged, IContactManifold<TManifold>;

        /// <summary>
        /// 在将子任务生成的结果报告给父任务之前,提供对子任务生成结果的控制。
        /// </summary>
        /// <param name="pairId">派生此子对的父对的ID。</param>
        /// <param name="Child A">属于考虑的子对中可碰撞A的子项的索引。</param>
        /// <param name="Child B">属于考虑的子对中可碰撞B的子项的索引。</param>
        /// <param name="manifold">要配置的子对的流形。</param>
        unsafe void OnChildPairCompleted(int pairId, int childA, int childB, ref ConvexContactManifold manifold);

        /// <summary>
        /// 检查是否应对给定子任务执行进一步的冲突测试。
        /// </summary>
        /// <param name="pairId">父对的ID。</param>
        /// <param name="Child A">属于考虑的子对中可碰撞A的子项的索引。</param>
        /// <param name="Child B">属于考虑的子对中可碰撞B的子项的索引。</param>
        /// <return>如果测试应继续,则为True;否则为False。</return>
        bool AllowCollisionTesting(int pairId, int childA, int childB);
    }


    public abstract class CollisionTask
    {
        /// <summary>
        /// 获取在执行此任务之前要一起批处理的任务数。
        /// </summary>
        public int BatchSize { get; protected set; }
        /// <summary>
        /// 获取与任务关联的第一个形状类型索引。提供给任务以供执行的形状对应按这些类型2索引定义的顺序进行。
        /// 如果碰撞任务不是顶级形状对任务,则应为-1。
        /// </summary>
        public int ShapeTypeIndexA { get; protected set; }
        /// <summary>
        /// 获取与任务关联的第二个形状类型索引。提供给任务以供执行的形状对应按这些类型2索引定义的顺序进行。
        /// 如果碰撞任务不是顶级形状对任务,则应为-1。
        /// </summary>
        public int ShapeTypeIndexB { get; protected set; }
        /// <summary>
        /// 获取任务是否能够生成子任务。请注意,子任务生成器不能生成子任务本身就是子任务生成器的子任务。
        /// </summary>
        public bool SubtaskGenerator { get; protected set; }
        /// <summary>
        /// 获取ExecuteBatch调用所需的对类型。
        /// </summary>
        public CollisionTaskPairType PairType { get; protected set; }

        // 请注意,我们没有定义任务执行的输入和输出的详细信息。
        // 任务可以进入批处理程序,并根据需要创建新条目或触发延续。
        /// <summary>
        /// 对给定输入执行任务。
        /// </summary>
        /// <typeparam name="TCallback">用于处理冲突任务结果的回调类型。</typeparam>
        /// <param name="Batcher">负责调用的Batcher。</param>
        /// <param name="Batch">要测试的配对批次。</param>
        /// <param name="Continuations">顶级对完成时要调用的延续。</param>
        /// <param name="Filters">用于影响冲突任务执行的筛选器。</param>
        public abstract void ExecuteBatch<TCallbacks>(ref UntypedList batch, ref CollisionBatcher<TCallbacks> batcher)
            where TCallbacks : struct, ICollisionCallbacks;

    }

    public enum CollisionTaskPairType
    {
        /// <summary>
        /// 具有完整姿势和翻转蒙版的两个形状的常规对,但没有相关数据的边界。
        /// </summary>
        StandardPair,
        /// <summary>
        /// 对专用于同一类型的两个形状之间的凸对。
        /// </summary>
        FliplessPair,
        /// <summary>
        /// 专用于两个球体的对,不需要翻转蒙版或方向。
        /// </summary>
        SpherePair,
        /// <summary>
        /// 对专用于包含一个不需要定向的球面的凸对。
        /// </summary>
        SphereIncludingPair,
        /// <summary>
        /// 对需要计算局部边界框,因此需要额外的信息,如速度。
        /// </summary>
        BoundsTestedPair

    }

    public struct CollisionTaskReference
    {
        public int TaskIndex;
        public int BatchSize;
        public int ExpectedFirstTypeId;
        public CollisionTaskPairType PairType;
    }

    public class CollisionTaskRegistry
    {
        CollisionTaskReference[][] topLevelMatrix;
        internal CollisionTask[] tasks;
        int count;

        public CollisionTask this[int taskIndex]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return tasks[taskIndex];
            }
        }

        public CollisionTaskRegistry(int initialShapeCount = 9)
        {
            ResizeMatrix(initialShapeCount);
        }

        void ResizeMatrix(int newSize)
        {
            var oldSize = topLevelMatrix != null ? topLevelMatrix.Length : 0;
            Array.Resize(ref topLevelMatrix, newSize);
            for (int i = 0; i < newSize; ++i)
            {
                Array.Resize(ref topLevelMatrix[i], newSize);
                for (int j = oldSize; j < newSize; ++j)
                {
                    topLevelMatrix[i][j] = new CollisionTaskReference { TaskIndex = -1 };
                }
            }
        }

        public int Register(CollisionTask task)
        {
            // 某些任务可以生成任务。请注意,这只能是一级深度;不允许嵌套复合。
            // 所有这样的发电机都将放置在开始处。
            var index = task.SubtaskGenerator ? 0 : count;

            // 由于频繁地调整大小,这会分配大量的垃圾,但是任务注册在程序初始化时是一次性的,这并不重要。
            // 严格的界限对于最终的性能更有用(因为堆稍微简单一些)。
            int newCount = count + 1;
            if (tasks == null || newCount > tasks.Length)
                Array.Resize(ref tasks, newCount);
            if (index < count)
            {
                Array.Copy(tasks, index, tasks, index + 1, count - index);
                // 数组中每个被移动的任务都需要将其顶层索引颠簸。
                for (int i = 0; i < topLevelMatrix.Length; ++i)
                {
                    for (int j = 0; j < topLevelMatrix.Length; ++j)
                    {
                        if (topLevelMatrix[i][j].TaskIndex >= index)
                        {
                            ++topLevelMatrix[i][j].TaskIndex;
                        }
                    }
                }
            }

            tasks[index] = task;
            count = newCount;

            var a = task.ShapeTypeIndexA;
            var b = task.ShapeTypeIndexB;
            var highestShapeIndex = a > b ? a : b;
            if (highestShapeIndex >= 0)
            {
                // 这只处理顶级对(与造型类型对相关联的对)。
                // 有些任务并不直接与顶级入口点相关联-相反,它们是顶级任务的后续。因为它们不是入口点,
                // 它们不需要出现在顶层矩阵中。
                if (highestShapeIndex >= topLevelMatrix.Length)
                    ResizeMatrix(highestShapeIndex + 1);
                var taskInfo = new CollisionTaskReference
                {
                    TaskIndex = index,
                    BatchSize = task.BatchSize,
                    ExpectedFirstTypeId = task.ShapeTypeIndexA,
                    PairType = task.PairType
                };
                topLevelMatrix[a][b] = taskInfo;
                topLevelMatrix[b][a] = taskInfo;
            }

#if DEBUG
            // 确保不存在任务依赖关系循环。
            bool encounteredNongenerator = false;
            for (int i = 0; i < count; ++i)
            {
                if (encounteredNongenerator)
                {
                    Debug.Assert(!tasks[i].SubtaskGenerator,
                        "To avoid cycles, the tasks list should be partitioned into two contiguous groups: subtask generators, followed by non-subtask generators.");
                }
                else
                {
                    if (!tasks[i].SubtaskGenerator)
                        encounteredNongenerator = true;
                }
            }
#endif           
            return index;

        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref CollisionTaskReference GetTaskReference(int topLevelTypeA, int topLevelTypeB)
        {
            return ref topLevelMatrix[topLevelTypeA][topLevelTypeB];
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref CollisionTaskReference GetTaskReference<TShapeA, TShapeB>()
            where TShapeA : unmanaged, IShape
            where TShapeB : unmanaged, IShape
        {
            return ref GetTaskReference(default(TShapeA).TypeId, default(TShapeB).TypeId);
        }
    }
}
